package com.migo.aop;


import com.migo.log.SystemLog;
import com.migo.thread.service.ISLSProducerLogService;
import com.migo.utils.DateUtils;
import com.migo.utils.IpUtils;
import com.migo.utils.JsonUtils;
import com.migo.utils.VectorWriter;
import com.migo.utils.spring.SpringUtils;
import org.apache.ibatis.javassist.*;
import org.apache.ibatis.javassist.bytecode.CodeAttribute;
import org.apache.ibatis.javassist.bytecode.LocalVariableAttribute;
import org.apache.ibatis.javassist.bytecode.MethodInfo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;


/**
 *
 */
@Aspect
@Component
public class SystemLogAspect {
    private String requestURL = ""; // 请求地址
    private String requestURI = ""; // 请求资源
    //    总结一下：URL是一种具体的URI，它不仅唯一标识资源，
// 而且还提供了定位该资源的信息。URI是一种语义上的抽象概念，
// 可以是绝对的，也可以是相对的，而URL则必须提供足够的信息来定位，
// 所以，是绝对的，而通常说的relative URL，则是针对另一个absolute URL，
// 本质上还是绝对的
    private String ip = "127.0.0.1"; // 请求ip
    private String inputParamJson = ""; // 传入参数
    private String outputParamJson = ""; // 返回结果
    private String requestMethodName = "";//请求方法名称
    private String requestMethodDesc = "";//请求方法描述
    private String operationType = "";//要执行的操作类型比如：add操作

    private long startTimeMillis = 0; // 开始时间
    private long endTimeMillis = 0; // 结束时间
    private String exceptionCode = "";//异常代码
    private String exceptionMessage = "";//异常信息
    private int logType = 0;//日志类型 0 正常日志 1 错误日志


    //Controller层切点
    @Pointcut("execution (* com.migo.api.*.*(..))")
    public void controllerAspect() {
    }

    /**
     * 前置通知 用于方法调用前
     *
     * @param joinPoint 切点
     */

    @Before("controllerAspect()")
    public void doBefore(JoinPoint joinPoint) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        requestURL = request.getRequestURL().toString();
        requestURI = request.getRequestURI();
        ip = IpUtils.getIpAddr(request);
        startTimeMillis = System.currentTimeMillis();
    }


    private Map<String, Object> getFieldsName(Class cls, String clazzName, String methodName, Object[] args) throws NotFoundException {
        Map<String, Object> map = new HashMap<>();

        if (!methodName.equals("setReqAndResp"))   //忽略售后的继承方法
        {

            ClassPool pool = ClassPool.getDefault();
            //ClassClassPath classPath = new ClassClassPath(this.getClass());
            ClassClassPath classPath = new ClassClassPath(cls);
            pool.insertClassPath(classPath);

            CtClass cc = pool.get(clazzName);
            CtMethod cm = cc.getDeclaredMethod(methodName);
            MethodInfo methodInfo = cm.getMethodInfo();
            CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
            LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
            if (attr == null) {
                // exception
            }
            // String[] paramNames = new String[cm.getParameterTypes().length];
            int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
            try {
                for (int i = 0; i < cm.getParameterTypes().length; i++) {
                    //                map.put(attr.variableName(i + pos), args[0]);//paramNames即参数名
                    //                map.put(methodName, args[0]);
                    if (args[0] != null) {
                        boolean bool = args[0].getClass().getName().equals("java.lang.String");
                        if (bool) {
                            map.put("params", args[0]);
                        } else {
                            map = (Map<String, Object>) args[0];
                        }
                    }
                }
            } catch (Exception e) {
                return new HashMap<>();
            }
        }
        return map;
    }

    /**
     * 后置通知 用于方法调用后
     *
     * @param joinPoint 切点
     */

    @After("controllerAspect()")
    public void after(JoinPoint joinPoint) throws Exception {
        try {
            String targetName = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            Object[] arguments = joinPoint.getArgs();
            Class targetClass = Class.forName(targetName);
            Map<String, Object> reqParams = getFieldsName(this.getClass(), targetClass.getName(), methodName, arguments);
            if (!reqParams.isEmpty()) {
                try {
                    inputParamJson = JsonUtils.getJSONString(reqParams);
                } catch (Exception e) {
//                    e.printStackTrace();
                    VectorWriter vectorWriter = new VectorWriter();
                    e.printStackTrace(vectorWriter);
                    String[] strings = vectorWriter.toStringArray();
                    StringBuilder stackTrackStringBuilder = new StringBuilder();
                    for (String str : strings) {
                        stackTrackStringBuilder.append(str + "\n");
                    }
                    inputParamJson = stackTrackStringBuilder.toString();
                }
            } else {
            }
            requestMethodName = targetName + "." + methodName;
            endTimeMillis = System.currentTimeMillis();
        } catch (Exception e) {
            VectorWriter vectorWriter = new VectorWriter();
            e.printStackTrace(vectorWriter);
            String[] strings = vectorWriter.toStringArray();
            StringBuilder stackTrackStringBuilder = new StringBuilder();
            for (String str : strings) {
                stackTrackStringBuilder.append(str + "\n");
            }
            inputParamJson = stackTrackStringBuilder.toString();
        }
    }


    /**
     * 异常通知 用于拦截记录异常日志
     *
     * @param e
     */
    @AfterThrowing(pointcut = "controllerAspect()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        logType = 1;
        exceptionCode = e.getClass().getName();
        exceptionMessage = e.getMessage();
    }

    //配置后置返回通知,使用在方法aspect()上注册的切入点

    @AfterReturning(returning = "rvt", pointcut = "controllerAspect()")
    public void afterReturn(Object rvt) {

        try {
            outputParamJson = rvt != null ? JsonUtils.getJSONString(rvt) : "";
            SystemLog log = new SystemLog();
            log.setRequestURL(requestURL);
            log.setRequestURI(requestURI);
            log.setIp(ip);
            log.setInputParamJson(inputParamJson);//请求参数
            log.setOutputParamJson(outputParamJson);//返回结果
            log.setRequestMethodName(requestMethodName);//方法名称
            log.setRequestMethodDesc(requestMethodDesc);//方法描述
            log.setOperationType(operationType);//要执行的操作类型 eg:add delete updata select
            log.setStartTimeMillis(Long.toString(startTimeMillis));//方法开始调用时间
            log.setEndTimeMillis(Long.toString(endTimeMillis));//方法结束调用时间
            log.setExceptionCode(exceptionCode);//异常代码
            log.setExceptionMessage(exceptionMessage);//异常信息
            log.setLogType(logType);
            log.setAddTime(DateUtils.getNow());//日志记录时间
            log.setLogId(UUID.randomUUID().toString());
            exceptionCode = "";
            exceptionMessage = "";
            logType = 0;
            SpringUtils.getBean(ISLSProducerLogService.class).sendSLSLogs("vision-plus", "applog", "topic1", "resource", JsonUtils.getJSONString(log));
            //push到消息队列中
        } catch (Exception e) {
            //参数异常也不进行异常处理
            System.out.println(e);
        }


    }


}
